package com.tjbklx1.redis.chap05;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.tjbklx1.redis.JedisUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.Tuple;
import redis.clients.jedis.ZParams;

/**
 * 统计
 * @author tjbklx1
 * @date 2018-12-6
 */
public class App54_LogStats {

	public static void main(String[] args) {
		Jedis conn = JedisUtil.getJedisWithDB();
		new App54_LogStats().testStats(conn);
	}

	public void testStats(Jedis conn) {
		System.out.println("\n----- testStats -----");
		System.out.println("Let's add some data for our statistics!");
		List<Object> r = null;
		for (int i = 0; i < 5; i++) {
			double value = (Math.random() * 11) + 5; //随机数
			r = updateStats(conn, "temp", "example", value);
		}
		System.out.println("We have some aggregate statistics: " + r);
		Map<String, Double> stats = getStats(conn, "temp", "example");
		System.out.println("Which we can also fetch manually:");
		System.out.println(stats);
		assert stats.get("count") >= 5;
	}

	/**
	 * 更新统计信息
	 * @param conn
	 * @param context
	 * @param type
	 * @param value
	 * @return
	 */
	public List<Object> updateStats(Jedis conn, String context, String type, double value) {
		int timeout = 5000;
		String destination = "stats:" + context + ':' + type; // 组织KEY stats:context:type
		String startKey = destination + ":start"; // 组织KEY stats:context:type:start
		
		long end = System.currentTimeMillis() + timeout;
		while (System.currentTimeMillis() < end) {
			conn.watch(startKey);
			String hourStart = MyLogger.ISO_FORMAT.format(new Date());

			String existing = conn.get(startKey);
			Transaction trans = conn.multi();
			if (existing != null && MyLogger.COLLATOR.compare(existing, hourStart) < 0) {
				trans.rename(destination, destination + ":last"); //重命名
				trans.rename(startKey, destination + ":pstart");
				trans.set(startKey, hourStart);
			}

			String tkey1 = UUID.randomUUID().toString();
			String tkey2 = UUID.randomUUID().toString();
			trans.zadd(tkey1, value, "min");
			trans.zadd(tkey2, value, "max");

			trans.zunionstore(destination, new ZParams().aggregate(ZParams.Aggregate.MIN), destination, tkey1);
			trans.zunionstore(destination, new ZParams().aggregate(ZParams.Aggregate.MAX), destination, tkey2);

			trans.del(tkey1, tkey2);
			trans.zincrby(destination, 1, "count");
			trans.zincrby(destination, value, "sum");
			trans.zincrby(destination, value * value, "sumsq");

			List<Object> results = trans.exec();
			if (results == null) {
				continue;
			}
			return results.subList(results.size() - 3, results.size());
		}
		return null;
	}

	/**
	 * 获得context的统计信息
	 * @param conn
	 * @param context
	 * @param type
	 * @return
	 */
	public Map<String, Double> getStats(Jedis conn, String context, String type) {
		String key = "stats:" + context + ':' + type;
		Map<String, Double> stats = new HashMap<String, Double>();
		Set<Tuple> data = conn.zrangeWithScores(key, 0, -1);
		for (Tuple tuple : data) {
			stats.put(tuple.getElement(), tuple.getScore());
		}
		stats.put("average", stats.get("sum") / stats.get("count")); //平均数
		double numerator = stats.get("sumsq") - Math.pow(stats.get("sum"), 2) / stats.get("count");
		double count = stats.get("count");
		stats.put("stddev", Math.pow(numerator / (count > 1 ? count - 1 : 1), .5)); //方差
		return stats;
	}
}
